package threadSpecificStorage;
import java.lang.ref.WeakReference;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * @Author: linKe
 * @Date: 2022/1/15 16:35
 * @FileName: M
 * @version: 1.0
 * @Description: 支持统一清理不再被使用的ThreadLocal变量的ThreadLocal的子类
 * @param <T> 存储的数据的类型
 *           看不懂 有空再来
 */
public class ManagedThreadLocal<T> {

    /**
     * volatile 当一个共享变量被volatile修饰时，它会保证修改的值会立即被更新到主存，
     * 当有其他线程需要读取时，它会去内存中读取新值。
     */
    private static volatile Queue<WeakReference<ManagedThreadLocal<?>>> instances =
            new ConcurrentLinkedQueue<WeakReference<ManagedThreadLocal<?>>>();

    private volatile ThreadLocal<T> threadLocal;

    private ManagedThreadLocal(InitialValueProvider<T> provider) {
        threadLocal = new ThreadLocal<T>() {
            @Override
            protected T initialValue() {
                return provider.initialValue();
            }
        };
    }

    public static <T> ManagedThreadLocal<T> newInstance(InitialValueProvider<T> provider) {
        ManagedThreadLocal<T> mtl = new ManagedThreadLocal<T>(provider);
        instances.add(new WeakReference<ManagedThreadLocal<?>>(mtl));
        return mtl;
    }

    public static <T> ManagedThreadLocal<T> newInstance() {
        return newInstance(new InitialValueProvider<T>());
    }

    public void set(T value) {
        threadLocal.set(value);
    }

    public T get() {
        return threadLocal.get();
    }

    public void remove() {
        threadLocal.remove();
        threadLocal = null;
    }

    /**
     * 清除所有的ThreadLocal实例，关键方法，可以在ServletContextListener的contextDestroyed方法中调用
     */
    public static void removeAll() {
        WeakReference<ManagedThreadLocal<?>> reference;
        ManagedThreadLocal<?> mtl = null;
        while ((reference = instances.poll()) != null) {
            mtl = reference.get();
            if (mtl != null) {
                mtl.remove();
            }
        }

    }

    /**
     * ThreadLocal初始化器
     * @author skywalker
     *
     * @param <T>
     */
    public static class InitialValueProvider<T> {
        protected T initialValue() {
            return null;
        }
    }

}
